/**
 * 为Element对象添加一个farthest方法，用于查找离指定元素最远的匹配元素
 * @param {unknown} selector 元素标签名
 * @returns
 */
Element.prototype.farthest = function (selector) {
  // 判断传入的参数是否为字符串，如果不是则返回null
  if (typeof selector !== "string") {
    return null;
  }
  // 调用closest方法查找离指定元素最近的匹配元素
  let eleMatch = this.closest(selector);
  // 定义一个变量用于存储返回的元素
  let eleReturn = null;
  // 当eleMatch不为null时，循环查找离指定元素最远的匹配元素
  while (eleMatch) {
    // 将eleMatch赋值给eleReturn
    eleReturn = eleMatch;
    // 调用closest方法查找eleMatch的父元素的匹配元素
    eleMatch = eleMatch.parentElement.closest(selector);
  }
  // 返回离指定元素最远的匹配元素
  return eleReturn;
};

/**
 * 最深的首（尾）节点（元素）
 */
Object.defineProperties(Node.prototype, {
  // firstNode属性，获取第一个非空文本节点
  firstNode: {
    get: function () {
      // 如果没有子节点，返回null
      if (!this.childNodes.length) {
        return null;
      }
      let result = null;
      // 定义一个递归函数step，用于遍历子节点
      const step = function (node) {
        // 获取第一个子节点
        let match = node.firstChild;
        // 如果第一个子节点是文本节点且内容为空，则获取下一个兄弟节点
        if (match && match.nodeType == 3 && match.nodeValue.trimStart() == "") {
          match = match.nextSibling;
        }
        // 如果有子节点，则将result赋值为该子节点，并递归调用step函数
        if (match) {
          result = match;
          step(match);
        }
      };
      // 调用step函数，从当前节点开始遍历
      step(this);
      // 返回result
      return result;
    },
  },
  // lastNode属性，获取最后一个非空文本节点
  lastNode: {
    get: function () {
      // 如果没有子节点，返回null
      if (!this.childNodes.length) {
        return null;
      }
      let result = null;
      // 定义一个递归函数step，用于遍历子节点
      const step = function (node) {
        // 获取最后一个子节点
        let match = node.lastChild;
        // 如果最后一个子节点是文本节点且内容为空，则获取上一个兄弟节点
        if (match && match.nodeType == 3 && match.nodeValue.trimEnd() == "") {
          match = match.previousSibling;
        }
        // 如果有子节点，则将result赋值为该子节点，并递归调用step函数
        if (match) {
          result = match;
          step(match);
        }
      };
      // 调用step函数，从当前节点开始遍历
      step(this);
      // 返回result
      return result;
    },
  },
});

Object.defineProperties(Element.prototype, {
  // 获取最深的第一个元素
  firstElement: {
    get: function () {
      if (!this.children.length) {
        return null;
      }

      let eleMatch = this.firstElementChild;
      let eleReturn = null;

      while (eleMatch) {
        eleReturn = eleMatch;
        eleMatch = eleMatch.firstElementChild;
      }

      return eleReturn;
    },
  },
  lastElement: {
    // 获取最深的最后一个元素
    get: function () {
      if (!this.children.length) {
        return null;
      }

      let eleMatch = this.lastElementChild;
      let eleReturn = null;

      while (eleMatch) {
        eleReturn = eleMatch;
        eleMatch = eleMatch.lastElementChild;
      }

      return eleReturn;
    },
  },
});

// 改变元素的标签方法
const propsTagName = Object.getOwnPropertyDescriptor(
  Element.prototype,
  "tagName"
);
Object.defineProperty(Element.prototype, "tagName", {
  ...propsTagName,
  set: function (name) {
    if (typeof name != "string" || name.toUpperCase() == this.tagName) {
      return;
    }

    const eleNew = document.createElement(name.toLowerCase());
    eleNew.append.apply(eleNew, [...this.childNodes]);

    this.replaceWith(eleNew);

    return name;
  },
});

// 获取所有的文本节点
Object.defineProperty(Element.prototype, "childTextNodes", {
  get: function () {
    // 获取所有的文本节点
    const nodeIterator = document.createNodeIterator(
      this,
      NodeFilter.SHOW_TEXT,
      (node) => {
        return node.textContent.trim()
          ? NodeFilter.FILTER_ACCEPT
          : NodeFilter.FILTER_REJECT;
      }
    );

    // 节点迭代器转为数组并返回
    const arrNodes = [];
    let node = nodeIterator.nextNode();
    while (node) {
      arrNodes.push(node);
      node = nodeIterator.nextNode();
    }

    return arrNodes;
  },
});

// 部分文字使用 HTML 包装
// selector 表示希望包裹的HTML元素选择器
// 仅支持标签和类名选择器
// text 表示希望包裹的字符，不设置表示整个文本节点
Text.prototype.surround = function (selector, text) {
  const textContent = this.nodeValue;
  if (!textContent || !selector) {
    return null;
  }

  text = text || textContent;

  // 包装的元素标签和类名
  const arrClass = selector.split(".");
  const tagName = arrClass[0] || "span";
  const className = arrClass.slice(1).join(" ");

  // 索引范围
  const startIndex = textContent.indexOf(text);

  if (startIndex == -1) {
    return null;
  }

  const range = document.createRange();
  range.setStart(this, startIndex);
  range.setEnd(this, startIndex + text.length);

  // 元素创建
  const eleSurround = document.createElement(tagName);
  if (className) {
    eleSurround.className = className;
  }

  // 执行最后一击
  range.surroundContents(eleSurround);

  return eleSurround;
};

// 合并等同元素
Element.prototype.merge = function (selector) {
  if (!selector) {
    return;
  }

  [...this.querySelectorAll(selector)].some((ele) => {
    // 如果和前面的节点类型一致，合并
    let nodePrev = ele.previousSibling;
    let elePrev = ele.previousElementSibling;

    if (!nodePrev || !elePrev) {
      return false;
    }

    // 非内联元素换行符忽略
    const display = getComputedStyle(ele).display;
    if (
      nodePrev.nodeType == 3 &&
      (nodePrev.nodeValue === "" ||
        (!/inline/.test(display) && !nodePrev.nodeValue.trim()))
    ) {
      nodePrev.remove();
      // 递归处理
      this.merge(selector);

      return true;
    }

    // 如果前面的节点也是元素，同时类名一致
    if (
      nodePrev &&
      nodePrev == elePrev &&
      ele.cloneNode().isEqualNode(nodePrev.cloneNode())
    ) {
      elePrev.append.apply(elePrev, [...ele.childNodes]);
      ele.remove();
      // 递归处理
      this.merge(selector);

      return true;
    }
  });
};
